home *** CD-ROM | disk | FTP | other *** search
- (c) Copyright 1989-1999 Amiga, Inc. All rights reserved.
- The information contained herein is subject to change without notice, and
- is provided "as is" without warranty of any kind, either expressed or implied.
- The entire risk as to the use of this information is assumed by the user.
-
-
-
- SoftInt MsgPorts and the Timer Device
-
- Carolyn Scheppner - CATS
-
-
-
- Most Amiga programmers are familiar with the PA_SIGNAL type of
- Exec MsgPort which signals a task when a message is received. This
- is the type of MsgPort that Intuition provides for IDCMP, and also
- the type commonly used for Exec device IO. The amiga.lib functions
- CreatePort() and DeletePort() are for PA_SIGNAL ports.
-
- A less familiar type of Exec MsgPort is the PA_SOFTINT port.
- PA_SOFTINT ports cause a software interrupt when a message is received,
- rather than signalling a task. Software interrupts execute at a priority
- lower than hardware interrupts but higher than tasks - that is, a software
- interrupt will pre-empt even a running task.
-
- For time-sensitive applications the timer.device will give more precise
- timings with a PA_SOFTINT port than with a PA_SIGNAL port. This is because
- the PA_SIGNAL port typically requires applications to Wait() for the device
- to respond either directly, or indirectly with a DoIO() call. Such a Wait()
- will switch out the calling task until the device has responded at which
- time the calling task will be placed in the ready queue awaiting its turn
- to run. Under heavy multi-tasking loads, this extra overhead can cause
- additional delay in interval timings leading many people to believe that
- the timer.device is not accurate. In reality, the timer.device can provide
- accurate timing even with intervals occuring several times per video
- frame. The trick is to eliminate the task-switching overhead by using a
- PA_SOFTINT port.
-
- This can provide timings which are more consistent under heavy multi-tasking
- loads. This technique could be useful for an application that needs to poll
- or control external hardware at regular intervals, and can also be used for
- simple incrementing of counters or program timers.
-
- Keep in mind that your software interrupt code should be kept as short and
- quick as possible, so that system and application tasks can get the time
- they need. Note that if a Disable() is in effect, no interrupts will be
- generated. If a Forbid() is in effect interrupts will still run normally.
-
- The audio chapter of Addison-Wesley's "ROM Kernel Manual: Libraries
- and Devices" gives an audio device example which demonstrates the use of
- a PA_SOFTINT port for the automatic manipulation of audio samples.
- The example listed here, TimerSoftInt.c, demonstrates how a PA_SOFTINT
- port can be used with the timer.device to produce software interrupts
- several times per frame. TimerSoftInt changes the screen color to display
- when and how often the timer software interrupt code is being executed.
- By adjusting the value passed to TimerSoftInt, you can find an interval
- which will cause the timer interrupts to occur at approximately the
- same vertical position each frame, allowing you to clearly see and test
- the consistency of the timing under different multi-tasking loads such as
- when running a compiler, for example.
-
-
- IMPORTANT
-
- Do not BeginIO() to any device other than timer or audio from within a
- software or hardware interrupt. The BeginIO() code of other devices may
- allocate memory, Wait(), or perform other functions which are illegal or
- dangerous during interrupts.
-
- The example below directly accesses the background color register so you
- can "see" when a timer interrupt occurs. An application should NEVER do
- this. Use direct access to Amiga hardware registers for debugging or
- demonstration purposes ONLY, as is the case here.
-
-
- --------------------------------------------------------------------------
- /*
- * TimerSoftInt.c - C. Scheppner C.A.T.S. 02/89
- * Copyright 1989-1999 Amiga, Inc. All Rights Reserved
- *
- * Demonstrates use of the timer.device with a PA_SOFTINT MsgPort
- * for applications that need to do processing on a regular basis,
- * several times per frame, in a multi-tasking environment.
- */
-
- #include "exec/types.h"
- #include "exec/interrupts.h"
- #include "exec/memory.h"
- #include "devices/timer.h"
- #include "libraries/dos.h"
- #include "hardware/custom.h"
-
- extern struct Custom custom;
-
- #define RED_CT 18
- #define MIC_DELAY 2500
- #define MIN_DELAY 1200
-
- extern VOID *tsoftcode(); /* our timer softint code */
-
- struct Interrupt *tint = NULL;
- struct timerequest *treq = NULL;
- struct MsgPort *tport = NULL;
-
- BOOL OpenedTimer = FALSE;
-
- /* Global variables shared with the softint code
- * If our tsoftcode was in assembler, we could use is_Data
- * pointer instead to tell softint code where shared data is
- */
- #define OFF 0
- #define ON 1
- #define STOPPED 2
-
- ULONG redct, micdelay;
-
- BOOL SFlag=OFF;
-
- char tportname[] = "CAS_CBM_timer_softint";
- char cprt[] =
- "Copyright (c) 1989-1999 Amiga, Inc. All Rights Reserved";
-
-
- main(argc,argv)
- int argc;
- char **argv;
- {
- if(!argc) exit(RETURN_FAIL); /* CLI only example...Uses printf */
-
- redct = RED_CT;
- micdelay = MIC_DELAY;
- if(argc>1)
- {
- if(argv[1][0]=='?') cleanexit("TimerSoftInt [mdelay]",RETURN_OK);
- else micdelay=atoi(argv[1]);
- if(micdelay < MIN_DELAY)
- {
- micdelay = MIN_DELAY;
- printf("Delay set to example minimum of %ld\n",MIN_DELAY);
- }
- }
-
- /* Allocate our MsgPort and Interrupt structures
- * Since we can't use CreatePort, we'll build the port ourselves.
- * Create Port creates a PA_SIGNAL and allocs a signal.
- * We want a PA_SOFTINT port;
- */
-
- if(!(tport = (struct MsgPort *)
- AllocMem(sizeof(struct MsgPort),MEMF_PUBLIC|MEMF_CLEAR)))
- cleanexit("Can't allocmem msgport",RETURN_FAIL);
-
- if(!(tint = (struct Interrupt *)
- AllocMem(sizeof(struct Interrupt),MEMF_PUBLIC|MEMF_CLEAR)))
- cleanexit("Can't allocmem interrupt",RETURN_FAIL);
-
-
- /* Set up the interrupt structure.
- * Note that we are priority 0. Software interrupts may only be
- * priority -32, -16, 0, +16, or +32. Also note that the
- * correct node type for a software interrupt is NT_INTERRUPT.
- * (NT_SOFTINT is an internal flag of Exec's)
- */
- tint->is_Code = (VOID (*)())tsoftcode; /* Our softint routine */
- tint->is_Node.ln_Type = NT_INTERRUPT;
- tint->is_Node.ln_Pri = 0;
-
- /* Set up the PA_SOFTINT msgport */
- tport->mp_Node.ln_Type = NT_MSGPORT;
- tport->mp_Node.ln_Name = (char *)tportname;
- tport->mp_Flags = PA_SOFTINT;
- tport->mp_SigTask = (struct Task *)tint; /* Ptr to interrupt struct */
-
- /* Not using CreatePort, so we must add the port ourselves */
- AddPort(tport);
-
- /* Now Create the IO request */
- if(!(treq=(struct timerequest *)
- CreateExtIO(tport,sizeof(struct timerequest))))
- cleanexit("Can't create ioreq", RETURN_FAIL);
-
- /* Open the timer device - Note 0 return means success */
- if(OpenDevice("timer.device",UNIT_MICROHZ,treq,0))
- cleanexit("Can't open timer device",RETURN_FAIL);
-
- OpenedTimer = TRUE; /* Flag for closing in cleanup */
-
- /* Now, let's do something with it */
- SFlag = ON;
- begintr(treq); /* Prime the pump with first timer request */
-
- printf("Started timer softint. Press CTRL/C to exit...");
- Wait(SIGBREAKF_CTRL_C);
- printf("\nReceived CTRL_C... Turning off softint\n");
-
- SFlag = OFF;
- while(SFlag != STOPPED) Delay(10);
-
- cleanup();
- exit(RETURN_OK);
- }
-
-
- /* Routine called as software interrupt */
- VOID *tsoftcode()
- {
- struct timerequest *tr;
- ULONG de;
-
- /* Remove our message from our port */
- tr = (struct timerequest *)GetMsg(tport);
-
- /* If main hasn't flagged us to stop, keep the ball rolling */
- if((tr)&&(SFlag==ON))
- {
- /* Hit background color register for demonstration purposes
- * and send the timer request out again.
- * IMPORTANT: This self-perpetuating technique of calling
- * BeginIO during a software interrupt may only be used with
- * the audio and timer device.
- */
- custom.color[0] = 0x0FF0;
- begintr(tr);
-
- /* Then hit the background color register again to show the
- * the duration of the rest of our softint routine (for/next).
- * Hitting registers like this is for debugging only!
- */
- custom.color[0] = 0x0F00;
- for(de=0; de < redct; de++); /* Our do-nothing softint routine */
-
- /* Back to default screen color */
- custom.color[0] = 0x005A;
- }
-
- /* Else flag main we have indeed stopped */
- else(SFlag=STOPPED);
- return(0);
- }
-
-
- /* begintr(tr)
- * Sets up and sends off timer request.
- * IMPORTANT: Do not BeginIO to any device other than timer or audio
- * from within a software or hardware interrupt. The BeginIO code
- * of other devices may allocate memory, wait, or perform other
- * functions which are illegal or dangerous during interrupts.
- */
- begintr(tr)
- struct timerequest *tr;
- {
- /* Set up the timer command */
- tr->tr_node.io_Command = TR_ADDREQUEST;
- tr->tr_time.tv_micro = micdelay;
- BeginIO(tr);
- }
-
- /* Prints message if any, cleans up, and exits */
- cleanexit(s,n)
- UBYTE *s;
- int n;
- {
- if(*s) printf("%s\n",s);
- cleanup();
- exit(n);
- }
-
- /* Close/deallocate everything opened/allocated */
- cleanup()
- {
- if(OpenedTimer) CloseDevice(treq);
- if(treq) DeleteExtIO(treq);
- if(tport)
- {
- RemPort(tport);
- FreeMem(tport,sizeof(struct timerequest));
- }
- if(tint) FreeMem(tint, sizeof(struct Interrupt));
- }
-
-
- atoi( s )
- char *s;
- {
- int num = 0;
- int neg = 0;
-
- if( *s == '+' ) s++;
- else if( *s == '-' )
- {
- neg = 1;
- s++;
- }
-
- while( *s >= '0' && *s <= '9' )
- {
- num = num * 10 + *s++ - '0';
- }
- if( neg ) return( - num );
- return( num );
- }
-
-
- /* end */
-
-
-
-